package com.neusoft.neuiotms.dcvm.service.impl;

import java.util.List;
import java.util.Map.Entry;

import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;

import org.apache.commons.lang3.StringUtils;
import org.quartz.CronExpression;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.neusoft.bizcore.schedule.service.JobMgrService;
import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.bizcore.web.utils.id.BaseIdGenerator;
import com.neusoft.neuiotms.dcvm.converter.SysTaskConverter;
import com.neusoft.neuiotms.dcvm.dto.SysTaskDTO;
import com.neusoft.neuiotms.dcvm.enums.SysTaskStatus;
import com.neusoft.neuiotms.dcvm.model.SysTask;
import com.neusoft.neuiotms.dcvm.repository.SysTaskRepository;
import com.neusoft.neuiotms.dcvm.service.SysTaskService;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@Transactional
public class SysTaskServiceImpl implements SysTaskService {

    @Autowired
    private SysTaskRepository sysTaskRepository;
    @Autowired
    private SysTaskConverter sysTaskConverter;
    @Autowired
    private JobMgrService jobMgrService;
    @Autowired
    private BaseIdGenerator sysTaskCodeGenerator;

    @Override
    public List<SysTaskDTO> findAll() {
        final List<SysTask> models = this.sysTaskRepository.findAll();
        return this.sysTaskConverter.toListDTO(models);
    }

    @Override
    public Page<SysTaskDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<SysTask> result = this.sysTaskRepository.search(pageable, searchable);

        final Page<SysTaskDTO> dtos = this.sysTaskConverter.toPageDTO(result);
        dtos.getContent().forEach(dto -> {
            final Searchable search = new Searchable();
            search.put("keyword", dto.getCode());
            final Page<Trigger> triggers = this.jobMgrService.getTriggers(search, pageable);
            if (triggers.getTotalElements() > 0) {
                dto.setPreviousTime(triggers.getContent().get(0).getPreviousFireTime());
                dto.setNextTime(triggers.getContent().get(0).getNextFireTime());
            }
        });
        return dtos;
    }

    @Override
    public SysTaskDTO create(final SysTaskDTO dto) {
        if (!CronExpression.isValidExpression(dto.getCron())) {
            throw new UnitedException("cron表达式不合法", new Object[] {});
        }
        dto.setCode(this.sysTaskCodeGenerator.generateCode());
        final SysTask model = this.sysTaskConverter.toModel(dto);
        this.sysTaskRepository.save(model);
        this.addJob(model);
        return this.sysTaskConverter.toDTO(model);
    }

    @Override
    public SysTaskDTO update(final Long id, final SysTaskDTO dto) {

        if (!CronExpression.isValidExpression(dto.getCron())) {
            throw new UnitedException("cron表达式不合法", new Object[] {});
        }
        SysTask model;
        try {
            model = this.sysTaskRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
        dto.setId(model.getId());
        model = this.sysTaskConverter.toModel(dto);
        this.sysTaskRepository.save(model);
        this.addJob(model);
        return this.sysTaskConverter.toDTO(model);
    }

    @Override
    public SysTaskDTO details(final Long id) {

        final SysTask model;
        try {
            model = this.sysTaskRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.sysTaskConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        final SysTask model;
        try {
            model = this.sysTaskRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }
        this.sysTaskRepository.deleteById(id);
        this.jobMgrService.pauseJob(model.getCode());
        this.jobMgrService.deleteJob(model.getCode());
    }

    @Override
    public void refreshJob() {
        final List<SysTask> tasks = this.sysTaskRepository.findAll();
        for (final SysTask scheduleJob : tasks) {
            this.addJob(scheduleJob);
        }
    }

    @SuppressWarnings({"unchecked" })
    private void addJob(final SysTask scheduleJob) {
        JobDetail jobDetail = null;
        this.jobMgrService.pauseJob(scheduleJob.getCode());
        this.jobMgrService.deleteJob(scheduleJob.getCode());

        if (SysTaskStatus.notActive.equals(scheduleJob.getStatus())) {
            return;
        }
        try {
            // 创建JobDetail（数据库中job_name存的任务全路径，这里就可以动态的把任务注入到JobDetail中）
            jobDetail = JobBuilder
                    .newJob((Class<? extends QuartzJobBean>) Class.forName(scheduleJob.getJobClass()))
                    .withIdentity(scheduleJob.getCode())
                    .withDescription(scheduleJob.getJobDesc())
                    .build();
            final String cron = scheduleJob.getCron();
            if (StringUtils.isNotBlank(scheduleJob.getJobData())) {
                final JSONObject obj = JSON.parseObject(scheduleJob.getJobData());
                for (final Entry<String, Object> entry : obj.entrySet()) {
                    jobDetail.getJobDataMap().put(entry.getKey(), entry.getValue());
                }
            }
            this.jobMgrService.scheduleCronJob(scheduleJob.getCode(), cron, jobDetail);
            SysTaskServiceImpl.log.info("定时任务添加成功：" + scheduleJob.getName());
        } catch (final ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void enable(Long id) {
        final SysTask model;
        try {
            model = this.sysTaskRepository.getOne(id);
            model.setStatus(SysTaskStatus.active);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        this.addJob(model);
    }

    @Override
    public void disable(Long id) {
        final SysTask model;
        try {
            model = this.sysTaskRepository.getOne(id);
            model.setStatus(SysTaskStatus.notActive);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        this.addJob(model);
    }
}
